嗨大家好,我是Eric ~ 在前端開發中,我們經常會遇到需要去重(去除重複元素)的場景,特別是當我們處理復雜的數據結構,例如物件陣列時。
接下來就來分享如何使用 JavaScript 的 Set 和一個自定義的函數來達到去重的目的,假設我們有一個物件陣列,如下:
const arr = [
{ a: 1, b: 2 },
{ a: 1, b: 2 },
{ a: 1, b: 2, c: { a: 1, b: 2 } },
{ a: 2, b: 1, c: { a: 2, b: 1 } }
];
物件陣列去重, 只要物件的key值相同,就視為相同物件
如果使用 set 集合來去重,需要使用 === 來判斷是否重復(NaN認為是相同的、0 and -0 則不同),所以在這裡使用 Set,即便判斷的物件內容相同,但是因為記憶體位置不同(物件的指向不同),所以不會被視為相同物件
const newArr = [ ...new Set(arr)];
console.log(newArr);
// 輸出:
[
{ a: 1, b: 2 },
{ a: 1, b: 2 },
{ a: 1, b: 2, c: { a: 1, b: 2 } },
{ a: 2, b: 1, c: { a: 2, b: 1 } }
]
// 沒有任何去重
所以在這裡我們嘗試用原始迴圈、**JSON.stringify()
**的方式來去重:
// 1. 使用淺拷貝來創建一個新的陣列(陣列中的物件依然會引用原陣列中對應物件的記憶體位置,代表不是同一個物件)
const newArr = [ ...arr ];
// 2. 外部迴圈用於遍歷新陣列中的每一個元素
for (let i = 0; i < newArr.length; i++) {
// 3. 內部迴圈用於與外部迴圈當前元素進行比較,以找出重複的物件
for (let j = i + 1; j < newArr.length; j++) {
// 4. 使用 JSON.stringify 將當前比較的兩個物件轉成字串
// 這樣做是為了能夠簡單地比較兩個物件是否具有相同的屬性和值
if (JSON.stringify(newArr[i]) === JSON.stringify(newArr[j])) {
// 5. 如果找到重複的物件,則從新陣列中刪除它
newArr.splice(j, 1);
// 6. 由於刪除了一個元素,陣列長度減少了,為了不讓 j 越界,將 j 減 1
j--;
}
}
}
// 7. 輸出去重後的新陣列。
console.log(newArr);
你可以發現 1、 2個物件順序一樣,內容也一樣,但是第3、4個物件雖然內容一樣,但是順序不一樣。
使用 JSON.stringify()
進行物件比較有一個限制,那就是它對於屬性順序需要一樣
// 輸出:
[
{ a: 1, b: 2 },
// { a: 1, b: 2 }, // 重複的部分被刪除
// 順序不一樣,所以沒辦法去重
{ a: 1, b: 2, c: { a: 1, b: 2 } },
{ b: 2, a: 1, c: { b: 2, a: 1 } }
]
所以我們可以選擇 使用一個比較函式來處理這個問題
// 主函數:判斷兩個值(val1 和 val2)是否相等
function equals(val1, val2) {
// 1. 檢查 val1 和 val2 是否為物件
// 如果其中一個不是物件,則使用 Object.is 進行比較
// Object.is 可以正確處理 NaN 和 -0 的情況
if (!isObject(val1) || !isObject(val2)) {
return Object.is(val1, val2);
}
// 2. 判斷 val1 和 val2 是否引用同一個記憶體位置
// 如果是,則它們一定是相等的
if (val1 === val2) return true;
// 3. 獲取 val1 和 val2 的所有 Key(屬性名)
const val1Keys = Object.keys(val1);
const val2Keys = Object.keys(val2);
// 4. 比較兩個物件的 Key 的數量,如果數量不同,則兩個物件不可能相等
if (val1Keys.length !== val2Keys.length) return false;
// 5. 逐一比較兩個物件的每一個 Key
for (const key of val1Keys) {
// 5.1 如果 val2 沒有 val1 的某個 Key,則兩者不相等
if (!val2Keys.includes(key)) return false;
// 5.2 遞迴地比較 val1 和 val2 的這一個 Key 對應的值
// 如果它們對應的值不相等,則整個 val1 和 val2 也不相等
if (!equals(val1[key], val2[key])) return false;
}
// 6. 經過上面所有的檢查,如果函數還沒有返回 false,則可以確定 val1 和 val2 是相等的
return true;
}
// 輔助函數:檢查一個值是否是物件
// 注意:在 JavaScript 中,null 也被視為一種物件,所以需要額外判斷
function isObject(value) {
return value !== null && typeof value === 'object';
}
console.log(newArr);
// 輸出:[ { a: 1, b: 2 }, { a: 1, b: 2, c: { a: 1, b: 2 } } ]
物件陣列去重在許多場景下都是非常有用的,比如:
我們探討了如何使用 Set 和自定義函數來進行物件陣列的去重操作,雖然 Set 是一個很好的工具,但它在處理物件時有其局限性。
所以,瞭解如何自定義函數來進行更精確的比較判斷是非常重要的,才能在不同的場景下有更多的選擇,使用最佳的方案,那麼這次的分享到這邊~謝謝大家~~~明天見!